/*
 * acooly.cn Inc.
 * Copyright (c) 2016 All Rights Reserved.
 * create by zhangpu 
 * date:2016年4月1日
 *
 */
package com.acooly.module.openapi.client.provider.fuyou.marshall;

import com.acooly.core.utils.Reflections;
import com.acooly.core.utils.Strings;
import com.acooly.module.openapi.client.api.exception.ApiClientException;
import com.acooly.module.openapi.client.api.marshal.ApiMarshal;
import com.acooly.module.openapi.client.provider.fuyou.domain.FuyouRequest;
import com.acooly.module.openapi.client.provider.fuyou.enums.FuyouServiceEnum;
import com.acooly.module.openapi.client.provider.fuyou.enums.FuyouServiceTypeEnum;
import com.acooly.module.openapi.client.provider.fuyou.support.FuyouAlias;
import com.acooly.module.openapi.client.provider.fuyou.utils.DESCoderFuyou;
import com.acooly.module.openapi.client.provider.fuyou.utils.StringHelper;
import com.acooly.module.openapi.client.provider.fuyou.utils.XmlUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.Feature;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.*;

/**
 * @author zhangpu
 */
@Service
@Slf4j
public class FuyouRequestMarshall extends FuyouMarshallSupport implements ApiMarshal<String, FuyouRequest> {

    @Override
    public String marshal(FuyouRequest source) {
        beforeMarshall(source);
        source.setMchntCd(source.getPartner());
        log.info("待签字符串:{}", source.getSignStr());
        String signature = doSign(source.getSignStr(),source.getService());
        source.setSign(signature);
        source.setMd5(signature);
        String requestData;
        if(FuyouServiceTypeEnum.STANDARD.equals(FuyouServiceEnum.find(source.getService()).getType())) {
            SortedMap<String, String> requestDataMap = getRequestDataMap(source);
            requestData = StringHelper.getRequestStr(requestDataMap);
        }else {
            String marshallData = XmlUtils.toXml(source,false);
            marshallData = XmlUtils.trimXml(marshallData);
            requestData = doMarshall(marshallData,source);
        }
        if(Strings.isBlank(requestData)) {
            throw new ApiClientException("请求报文为空");
        }
        log.info("请求报文:{}", requestData);
        return requestData;
    }

    /**
     * 提现请求报文组装
     * @param source
     * @return
     */
    public String withdrawMarshal(FuyouRequest source) {
        try {
            beforeMarshall(source);
            String partnerId = source.getPartner();
            String signStr = getWithdrawSignStr(source);
            source.setPartner(null);
            source.setSignType(null);
            String marshallData = XmlUtils.toXml(source, true);
            marshallData = XmlUtils.trimXml(marshallData);
//            marshallData = URLEncoder.encode(marshallData, "UTF-8");
            signStr = signStr + "|" + marshallData;
            log.info("待签字符串:{}", signStr);
            String mac = doSign(signStr, source.getService());
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("merid").append("=").append(partnerId).append("&");
            stringBuilder.append("reqtype").append("=").append(source.getReqtype()).append("&");
            stringBuilder.append("xml").append("=").append(marshallData).append("&");
            stringBuilder.append("mac").append("=").append(mac);
            String requestData = stringBuilder.toString();
            if (Strings.isBlank(requestData)) {
                throw new ApiClientException("请求报文为空");
            }
            log.info("请求报文:{}", requestData);
            return requestData;
        }catch (Exception e) {
            log.info("请求报文加密失败：{}",e.getMessage());
            return null;
        }
    }

    private String doMarshall(String requestData,FuyouRequest source){
        try {
            StringBuilder stringBuilder = new StringBuilder();
            if(FuyouServiceTypeEnum.APIFMS.equals(FuyouServiceEnum.find(source.getService()).getType())) {
                String desRequestData = DESCoderFuyou.desEncrypt(requestData, DESCoderFuyou.getKeyLength8(getSignKey(source.getService())));
                String mchntch = source.getPartner();
                String apiInfms = URLEncoder.encode(desRequestData, "UTF-8");
                stringBuilder.append("MCHNTCD").append("=").append(mchntch).append("&").append("APIFMS").append("=").append(apiInfms);
            }else if(FuyouServiceTypeEnum.FMS.equals(FuyouServiceEnum.find(source.getService()).getType())) {
                String apiInfms = URLEncoder.encode(requestData, "UTF-8");
                stringBuilder.append("FMS").append("=").append(apiInfms);
            } else if(FuyouServiceTypeEnum.FM.equals(FuyouServiceEnum.find(source.getService()).getType())){
                String apiInfms = URLEncoder.encode(requestData, "UTF-8");
                stringBuilder.append("FM").append("=").append(apiInfms);
            }
            return stringBuilder.toString();
        }catch (Exception e) {
            log.info("请求报文加密失败：{}",e.getMessage());
            return null;
        }
    }

    /**
     * 获取代签map
     *
     * @param source
     * @return
     */
    protected SortedMap<String, String> getRequestDataMap(FuyouRequest source) {
        SortedMap<String, String> signData = Maps.newTreeMap();
        Set<Field> fields = Reflections.getFields(source.getClass());
        String key = null;
        Object value = null;
        for (Field field : fields) {
            value = Reflections.getFieldValue(source, field.getName());
            if (value == null) {
                continue;
            }
            FuyouAlias baofuAlias = field.getAnnotation(FuyouAlias.class);
            if (baofuAlias == null) {
                continue;
            } else {
                if (!baofuAlias.sign()) {
                    continue;
                }
                key = baofuAlias.value();
            }
            if (Strings.isNotBlank((String) value)) {
                signData.put(key, (String) value);
            }
        }
        return signData;
    }

    /**
     * 获取提现代签字符串
     * @param source
     * @return
     */
    private String getWithdrawSignStr(FuyouRequest source) {
        String signStr = source.getPartner()+"|"+getSignKey(source.getService())+"|"+source.getReqtype();
        return signStr;
    }
}
